package org.nimi317.web_gis.task;

import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.nimi317.web_gis.entity.Job;
import org.nimi317.web_gis.service.IJobService;
import org.nimi317.web_gis.service.IUpdateService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.config.Task;
import org.springframework.scheduling.config.TriggerTask;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * @author thunderobot
 */
@Component
@Slf4j
public class ScheduledTask implements SchedulingConfigurer {

    private ScheduledTaskRegistrar registrar;

    private final IJobService jobService;

    private final ApplicationContext context;

    private final ThreadPoolTaskExecutor executor;

    private final IUpdateService service;

    private final AirQualityRealTask task;

    private final Map<Integer, org.springframework.scheduling.config.ScheduledTask> taskMap = new LinkedHashMap<>();

    public ScheduledTask(@Lazy IJobService jobService, @Lazy ApplicationContext context, ThreadPoolTaskExecutor executor, IUpdateService service, @Lazy AirQualityRealTask task) {
        this.jobService = jobService;
        this.context = context;
        this.executor = executor;
        this.service = service;
        this.task = task;
    }

    @Override
    public void configureTasks(@NotNull ScheduledTaskRegistrar taskRegistrar) {
        this.registrar = taskRegistrar;
        this.init();
        log.info("初始化定时任务");
        this.after();
    }

    private void init() {
        List<Job> jobs = jobService.getAllJobs();
        for (Job job : jobs) {
            add(job.getId(), getTask(job));
        }
    }

    private TriggerTask getTask(Job job) {
        Object bean = context.getBean(job.getBean());
        Integer id = job.getId();
        return new TriggerTask(() -> {
            try {
                Method method = bean.getClass().getMethod(job.getMethod());
                method.setAccessible(true);
                executor.execute(() -> {
                    try {
                        method.invoke(bean);
                    } catch (IllegalAccessException | InvocationTargetException e) {
                        throw new RuntimeException(e);
                    }
                });
            } catch (NoSuchMethodException e) {
                throw new RuntimeException(e);
            }
        }, triggerContext -> new CronTrigger(jobService.getJobById(id).getCron()).nextExecution(triggerContext));
    }


    /**
     * 添加定时任务
     */
    public void add(int id, TriggerTask task) {
        //如果已经存在 则暂停之前的
        org.springframework.scheduling.config.ScheduledTask before = this.taskMap.get(id);
        if (before != null) {
            before.cancel(false);
        }
        org.springframework.scheduling.config.ScheduledTask scheduledTask = this.registrar.scheduleTriggerTask(task);
        this.taskMap.put(id, scheduledTask);
        log.info("定时任务已添加, id:{}", id);
    }

    /**
     * 启动定时任务
     */
    public void start(int id) {
        org.springframework.scheduling.config.ScheduledTask task = this.taskMap.get(id);
        if (task != null) {
            Task taskTask = task.getTask();
            add(id, (TriggerTask) taskTask);
        } else {
            Job job = this.jobService.getJobById(id);
            add(job.getId(), getTask(job));
        }
        log.info("定时任务启动，id:{}", id);
    }

    /**
     * 停止定时任务
     */
    public void stop(int id) {
        org.springframework.scheduling.config.ScheduledTask task = this.taskMap.get(id);
        if (task != null) {
            task.cancel(false);
        }
        log.info("定时任务停止，id:{}", id);
    }

    public void remove(int id) {
        stop(id);
        this.taskMap.remove(id);
        log.info("定时任务移除，id:{}", id);
    }

    private void after() {
        if (!service.isUpdated()) {
            task.setUpdate(false);
            task.task();
        } else {
            task.setUpdate(true);
        }
    }
}
